package com.dcxs.shop.module.order.controller;


import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Console;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.domain.AlipayTradeRefundModel;
import com.alipay.api.domain.AlipayTradeWapPayModel;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.AlipayTradeRefundRequest;
import com.alipay.api.request.AlipayTradeWapPayRequest;
import com.alipay.api.response.AlipayTradeRefundResponse;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.dcxs.shop.common.annotation.UnAuthRequest;
import com.dcxs.shop.common.sdk.*;
import com.dcxs.shop.common.utils.*;
import com.dcxs.shop.config.AlipayConfig;
import com.dcxs.shop.module.order.dto.Cart;
import com.dcxs.shop.module.order.dto.Logistics;
import com.dcxs.shop.module.order.entity.*;
import com.dcxs.shop.module.order.service.*;
import com.dcxs.shop.module.product.entity.JsProduct;
import com.dcxs.shop.module.product.entity.JsProductPay;
import com.dcxs.shop.module.product.entity.JsProductPrice;
import com.dcxs.shop.module.product.service.IJsProductPayService;
import com.dcxs.shop.module.product.service.IJsProductPriceService;
import com.dcxs.shop.module.product.service.IJsProductService;
import com.dcxs.shop.module.user.entity.JsUsr;
import com.dcxs.shop.module.user.form.SendMessageForm;
import com.dcxs.shop.module.user.service.IJsUsrService;
import com.github.binarywang.wxpay.bean.notify.WxPayNotifyResponse;
import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult;
import com.github.binarywang.wxpay.bean.order.WxPayMpOrderResult;
import com.github.binarywang.wxpay.bean.order.WxPayMwebOrderResult;
import com.github.binarywang.wxpay.bean.request.BaseWxPayRequest;
import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.service.WxPayService;
import io.swagger.annotations.ApiModelProperty;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Async;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 订单表 前端控制器
 * </p>
 *
 * @author zzh
 * @since 2020-03-01
 */
@RestController
@RequestMapping("/order/js-product-order")
public class JsProductOrderController {

    private WxPayService wxService;

    public JsProductOrderController(WxPayService wxService) {
        this.wxService = wxService;
    }

    @Autowired
    private IJsProductOrderService productOrderService;

    @Autowired
    private IJsProductOrderDetailService productOrderDetailService;

    @Autowired
    private IJsProductPriceService productPriceService;

    @Autowired
    private IJsCartService cartService;

    @Autowired
    private IJsUsrService usrService;

    @Autowired
    private IJsProductPayService productPayService;

    @Autowired
    private IJsProductOrderExpressService productOrderExpressService;

    @Autowired
    private RedisUtils redisUtils;

    @Autowired
    private IJsProductService productService;

    @Autowired
    private SendMessageUtils sendMessageUtils;

    @Value("${dcxs.message.payTemplateId}")
    public String payTemplateId;

    @Autowired
    private IJsExpressService expressService;



    @PostMapping("/add")
    @Transactional(rollbackFor = Exception.class)
    public R add(@RequestBody Cart cart, HttpServletRequest request){
        Integer  userId = usrService.getUserIdByPhone(request.getAttribute("userId").toString());
        List<JsProductOrderDetail> jsProductOrderDetails  = new ArrayList<>();
        BigDecimal totalPrice = new BigDecimal("0");
        BigDecimal totalPoints = new BigDecimal("0");
        BigDecimal totalWeight = new BigDecimal("0");
        BigDecimal totalExpree = new BigDecimal("0");
        String msg = "";
        if(1==cart.getIsUse()){
            JsProductOrderDetail productOrderDetail=null;
            for(JsCart cart1:cart.getJsCart()){
                BigDecimal onePrice = new BigDecimal("0");
                 productOrderDetail = new JsProductOrderDetail();
                 if(1==cart1.getPayList().size()){
                     if(1==cart1.getPayList().get(0).getType()){
                        //现金支付  单价*数量
                         JsProductPrice productPrice = productPriceService.getById(cart1.getPriceId());
                         onePrice = productPrice.getPrice();
                         totalPrice = onePrice.multiply(new BigDecimal(cart1.getNum())).add(totalPrice);
                         productOrderDetail.setPriceType(productPrice.getType());
                         productOrderDetail.setIsUse(0);
                         //查询库存
                         if(cart1.getNum()>productPrice.getStock()){
                             JsProduct product = productService.getById(cart1.getProductId());
                             if(null!=product){
                                 msg = msg + " " +product.getName() +"的"+ cart1.getSpecificationName();
                             }
                         }
                     }else {
                        //积分支付
                         JsProductPrice productPrice = productPriceService.getById(cart1.getPriceId());
                         onePrice = productPrice.getPrice();
                         totalPoints = onePrice.multiply(new BigDecimal(cart1.getNum())).add(totalPoints);
                         productOrderDetail.setPriceType(productPrice.getType());
                         productOrderDetail.setIsUse(1);
                         //查询库存
                         if(cart1.getNum()<productPrice.getStock()){
                             JsProduct product = productService.getById(cart1.getId());
                             if(null!=product){
                                 msg = msg + " " +product.getName() +"的"+ cart1.getSpecificationName();
                             }
                         }else {
                             productPrice.setStock(productPrice.getStock()-cart1.getNum());
                             productPriceService.updateById(productPrice);
                         }
                     }
                 }else {
                       //现金和积分都行，但是积分优先
                     JsProductPrice productPrice = productPriceService.getById(cart1.getPriceId());
                     onePrice = productPrice.getPrice();
                     totalPoints = onePrice.multiply(new BigDecimal(cart1.getNum())).add(totalPoints);
                     productOrderDetail.setPriceType(productPrice.getType());
                     productOrderDetail.setIsUse(1);
                     //查询库存
                     if(cart1.getNum()>productPrice.getStock()){
                         JsProduct product = productService.getById(cart1.getProductId());
                         if(null!=product){
                             msg = msg + " " +product.getName() +"的"+ cart1.getSpecificationDetailName();
                         }
                     }else {
                         productPrice.setStock(productPrice.getStock()-cart1.getNum());
                         productPriceService.updateById(productPrice);
                     }
                 }
                 if(null!=cart1.getShareUsrId()){
                     JsUsr shareUsr = usrService.getById(cart1.getShareUsrId());
                     productOrderDetail.setShareUsrId(cart1.getShareUsrId());
                     if(null!=shareUsr&&StringUtils.isNotBlank(shareUsr.getPhone())){
                         productOrderDetail.setShareUsrPhone(shareUsr.getPhone());
                     }
                 }
                 if(null != cart1.getWeight() && 0 == cart1.getIsFreeShipping()){
                     totalWeight=totalWeight.add(cart1.getWeight());
                 }
                productOrderDetail.setNum(cart1.getNum());
                productOrderDetail.setPrice(cart1.getPrice());
                productOrderDetail.setProductId(cart1.getProductId());
                productOrderDetail.setTotalPrice(onePrice.multiply(new BigDecimal(cart1.getNum())));
                productOrderDetail.setPriceId(cart1.getPriceId());
                productOrderDetail.setSpecificationId(cart1.getSpecificationId());
                productOrderDetail.setSpecificationName(cart1.getSpecificationName());
                productOrderDetail.setProductName(cart1.getProductName());
                productOrderDetail.setProductPic(cart1.getProductPic()==null?"":cart1.getProductPic());
                if(null!=cart1.getSupplier()){
                    productOrderDetail.setSupplierId(cart1.getSupplier().getId());
                    productOrderDetail.setSupplierName(cart1.getSupplier().getName());
                }
                if(StringUtils.isNotBlank(cart1.getProductPic())){
                    productOrderDetail.setProductPic(cart1.getProductPic());
                }
                jsProductOrderDetails.add(productOrderDetail);
            }
            if(StringUtils.isNotBlank(msg)){
                return R.error(msg+" 库存不足 ");
            }
        }else {
            JsProductOrderDetail productOrderDetail=null;
            JsProductPrice productPrice = null;
            for(JsCart cart1:cart.getJsCart()){
                BigDecimal onePrice = null;
                productOrderDetail = new JsProductOrderDetail();
                if(1==cart1.getPayList().size()){
                    if(1==cart1.getPayList().get(0).getType()){
                        //现金
                        productPrice = productPriceService.getById(cart1.getPriceId());
                        onePrice = productPrice.getPrice();
                        totalPrice = onePrice.multiply(new BigDecimal(cart1.getNum())).add(totalPrice);
                        productOrderDetail.setIsUse(0);
                    }else {
                        //积分
                        productPrice = productPriceService.getById(cart1.getPriceId());
                        onePrice = productPrice.getPrice();
                        totalPoints = onePrice.multiply(new BigDecimal(cart1.getNum())).add(totalPoints);
                        productOrderDetail.setIsUse(1);
                    }
                }else {
                    //现金
                     productPrice = productPriceService.getById(cart1.getPriceId());
                    onePrice = productPrice.getPrice();
                    totalPrice = onePrice.multiply(new BigDecimal(cart1.getNum())).add(totalPrice);
                    productOrderDetail.setIsUse(0);
                }
                if(null!=cart1.getShareUsrId()){
                    JsUsr shareUsr = usrService.getById(cart1.getShareUsrId());
                    productOrderDetail.setShareUsrId(cart1.getShareUsrId());
                    if(null!=shareUsr&&StringUtils.isNotBlank(shareUsr.getPhone())){
                        productOrderDetail.setShareUsrPhone(shareUsr.getPhone());
                    }
                }
                //邮费
                if(null != cart1.getWeight() && 0 == cart1.getIsFreeShipping()){
                    totalWeight=totalWeight.add(cart1.getWeight());
                }
                productOrderDetail.setNum(cart1.getNum());
                productOrderDetail.setPrice(cart1.getPrice());
                productOrderDetail.setProductId(cart1.getProductId());
                productOrderDetail.setTotalPrice(onePrice.multiply(new BigDecimal(cart1.getNum())));
                productOrderDetail.setPriceId(cart1.getPriceId());
                productOrderDetail.setSpecificationId(cart1.getSpecificationId());
                productOrderDetail.setPriceType(productPrice.getType());
                productOrderDetail.setSpecificationName(cart1.getSpecificationName());
                productOrderDetail.setProductName(cart1.getProductName());
                productOrderDetail.setProductPic(cart1.getProductPic());
                if(null!=cart1.getSupplier()){
                    productOrderDetail.setSupplierId(cart1.getSupplier().getId());
                    productOrderDetail.setSupplierName(cart1.getSupplier().getName());
                }
                if(StringUtils.isNotBlank(cart1.getProductPic())){
                    productOrderDetail.setProductPic(cart1.getProductPic());
                }
                jsProductOrderDetails.add(productOrderDetail);
                //查询库存
                if(cart1.getNum()>productPrice.getStock()){
                    JsProduct product = productService.getById(cart1.getProductId());
                    if(null!=product){
                        msg = msg + " " +product.getName() +"的"+ cart1.getSpecificationName();
                    }
                }else {
                    productPrice.setStock(productPrice.getStock()-cart1.getNum());
                    productPriceService.updateById(productPrice);
                }
            }
            if(StringUtils.isNotBlank(msg)){
                return R.error(msg+" 库存不足 ");
            }
        }
        JsProductOrder jsProductOrder = new JsProductOrder();
        List<JsExpress> expressesList = expressService.list();
        BigDecimal firstPrice = new BigDecimal("0");
        BigDecimal addPrice = new BigDecimal("0");
        if (2 == expressesList.size()) {
            for (JsExpress express : expressesList) {
                if (1 == express.getType()) {
                    firstPrice = express.getFee();
                } else {
                    addPrice = express.getFee();
                }
            }
        }
        if(totalWeight.compareTo(new BigDecimal("0"))==0){
            totalExpree=new BigDecimal("0");
        }
        else if((totalWeight.divide(new BigDecimal("1000"))).compareTo(new BigDecimal("1"))==-1){
            totalExpree=firstPrice;
        }else {
            totalExpree=firstPrice.add(addPrice.multiply(totalWeight.divide(new BigDecimal("1000")).subtract(new BigDecimal("1"))));
        }
        jsProductOrder.setName(cart.getAddress().getName()==null?"":cart.getAddress().getName());
        jsProductOrder.setPayWay(cart.getPayWay()==null?0:cart.getPayWay());
        jsProductOrder.setCashPayType(cart.getPayWay()==null?0:cart.getPayWay());
        jsProductOrder.setOrderNumber(OrderIdUtils.getOrderIdByUUId());
        if(1==cart.getPayWay()){
            jsProductOrder.setCashPayName("微信");
        }else if(2==cart.getPayWay()) {
            jsProductOrder.setCashPayName("支付宝");
        }else if(3==cart.getPayWay()){
            jsProductOrder.setCashPayName("银联支付");
        }else if(4==cart.getPayWay()){
            jsProductOrder.setCashPayName("线下支付");
        }else {
            jsProductOrder.setCashPayName("积分");
        }
        jsProductOrder.setUsrPhone(request.getAttribute("userId").toString());
        jsProductOrder.setRemark(cart.getRemark()==null?"":cart.getRemark());
        jsProductOrder.setPhone(cart.getAddress().getPhone()==null?"":cart.getAddress().getPhone());
        jsProductOrder.setAddress(cart.getAddress().getAddress()==null?"":cart.getAddress().getAddress());
        jsProductOrder.setArea(cart.getAddress().getArea()==null?"":cart.getAddress().getArea());
        jsProductOrder.setCity(cart.getAddress().getCity()==null?"":cart.getAddress().getCity());
        jsProductOrder.setProvince(cart.getAddress().getProvince()==null?"":cart.getAddress().getProvince());
        jsProductOrder.setIsUse(cart.getIsUse());
        jsProductOrder.setUsePointState(cart.getIsUse());
        jsProductOrder.setState(0);
        jsProductOrder.setTotalShippingPay(new BigDecimal(NumberUtil.roundStr(totalExpree.toString(),0)));
        jsProductOrder.setTotalPayPrice(NumberUtil.round(totalPrice.add(jsProductOrder.getTotalShippingPay()),2));
        jsProductOrder.setTotalPoints(NumberUtil.round(totalPoints,2));
        jsProductOrder.setUsePoints(NumberUtil.round(totalPoints,2));
        jsProductOrder.setTotalPrice(NumberUtil.round(totalPrice.add(totalPoints).add(jsProductOrder.getTotalShippingPay()),2));
        jsProductOrder.setUsrId(userId);
        jsProductOrder.setCreateTime(LocalDateTime.now());
        jsProductOrder.setInsertDatetime(LocalDateTime.now());
        jsProductOrder.setInsertDate(new Date());
        productOrderService.save(jsProductOrder);
        for(JsProductOrderDetail detail:jsProductOrderDetails){
            detail.setOrderNumber(jsProductOrder.getOrderNumber());
            detail.setOrderId(jsProductOrder.getId());
        }
        productOrderDetailService.saveBatch(jsProductOrderDetails);
        List<Integer>  cartIds = cart.getJsCart().stream().map(a ->a.getId()).collect(Collectors.toList());
        if(CollectionUtil.isNotEmpty(cartIds)){
            cartService.removeByIds(cartIds);
        }
        return R.ok().put("data",jsProductOrder.getId());
    }


    @Async
    public void sendPayTemplate(HttpServletRequest request, JsProductOrder jsProductOrder) {
        //发送短信
        JSONObject param = JSONUtil.createObj();
        param.put("code","LJSBX"+jsProductOrder.getOrderNumber());
        Console.log(("LJSBX"+jsProductOrder.getOrderNumber()).length());
        SendMessageForm sendMessageForm = new SendMessageForm();
        sendMessageForm.setMobile(request.getAttribute("userId").toString());
        sendMessageForm.setTemplateCode(payTemplateId);
        sendMessageForm.setTemplateParam(param.toString());
        String res = sendMessageUtils.send(sendMessageForm);
        Console.log(res);
    }

    @RequestMapping("/sendPayTemplate")
    public R sendPayTemplate(@RequestParam String orderId,HttpServletRequest request){
        JsProductOrder productOrder = productOrderService.getById(orderId);
        if(null!=productOrder){
            this.sendPayTemplate(request,productOrder);
            return R.ok();
        }
        return R.error();
    }

    @ApiModelProperty("准备支付")
    @PostMapping("/parePay")
    public R parePay(@RequestBody Cart cart, HttpServletRequest request){
        if(null!=cart.getOrderId()){
            JsProductOrder order = productOrderService.getById(cart.getOrderId());
            Integer  userId = usrService.getUserIdByPhone(request.getAttribute("userId").toString());
            BigDecimal points = usrService.getById(userId).getBalance()==null?new BigDecimal("0"): usrService.getById(userId).getBalance();
            //判断余额积分,够的话就PASS，不够看订单中积分支付的东西能否使用现金，能就加现金，不能就return
            if(points.compareTo(order.getUsePoints())==-1){
                List<JsProductOrderDetail> productOrderDetails = productOrderDetailService.list(new QueryWrapper<JsProductOrderDetail>()
                        .eq("order_id",order.getId()));
                //只支持积分的金额
                BigDecimal totalPoints = new BigDecimal("0");
                //两种都支持的金额
                BigDecimal allPoints = new BigDecimal("0");
                for(JsProductOrderDetail productOrderDetail:productOrderDetails){
                    if(1==productOrderDetail.getIsUse()){
                        //在下单的时候看到用了积分
                        //查询支持的支付方式
                        List<JsProductPay> payList = productPayService.list(new QueryWrapper<JsProductPay>()
                                .eq("product_id",productOrderDetail.getProductId()));
                        BigDecimal onePirce = productPriceService.getById(productOrderDetail.getPriceId()).getPrice();
                        if(CollectionUtil.isNotEmpty(payList)){
                            if(2==payList.size()){
                                //说明2种都支持
                                allPoints = allPoints.add(onePirce.multiply(new BigDecimal(productOrderDetail.getNum())));
                            }else {
                                //只支持积分
                                totalPoints=totalPoints.add(onePirce.multiply(new BigDecimal(productOrderDetail.getNum())));
                            }
                        }
                    }
                }
                if(totalPoints.compareTo(points)==1){
                    return new R().put("type",3);
                }else {
                    //积分够付
                    if(allPoints.compareTo(points)==-1||allPoints.compareTo(points)==0){
                        order.setTruePay(order.getTotalPrice().subtract(order.getUsePoints()));
                        order.setTruePayPoints(order.getTotalPrice().subtract(order.getTruePay()));
                        order.setUsePoints(order.getTotalPrice().subtract(order.getTruePay()));
                    }else {
                        //积分不够付 拿钱凑
                        order.setTruePay(order.getTotalPayPrice().add(order.getUsePoints().subtract(points)));
                        order.setTruePayPoints(order.getTotalPrice().subtract(order.getTruePay()));
                        order.setUsePoints(order.getTotalPrice().subtract(order.getTruePay()));
                    }
                }
                if(order.getTruePay().compareTo(new BigDecimal("0"))==0){
                    dealOnlyPointsPay(order);
                }
                productOrderService.updateById(order);
                return new R().put("payPrice",order.getTruePay()).put("type",1);
            }else {
                order.setTruePay(order.getTotalPrice().subtract(order.getUsePoints()));
                order.setTruePayPoints(order.getUsePoints());
                order.setUsePoints(order.getTotalPrice().subtract(order.getTruePay()));
                if(order.getTruePay().compareTo(new BigDecimal("0"))==0){
                    dealOnlyPointsPay(order);
                }
                productOrderService.updateById(order);
                return new R().put("payPrice",order.getTruePay()).put("type",2);
            }
        }
        return R.error();
    }


    @ApiModelProperty("支付宝支付")
    @GetMapping("/pay")
    public void pay(HttpServletRequest request, HttpServletResponse response){
        JsProductOrder productOrder = productOrderService.getById(request.getParameter("orderId"));
        if(null!=productOrder){
            String subject ="金山商城下单";
            String product_code="QUICK_WAP_WAY";
            String total_amount = productOrder.getTruePay().toString();
            AlipayClient client = new DefaultAlipayClient(AlipayConfig.URL, AlipayConfig.APPID, AlipayConfig.RSA_PRIVATE_KEY, AlipayConfig.FORMAT, AlipayConfig.CHARSET, AlipayConfig.ALIPAY_PUBLIC_KEY,AlipayConfig.SIGNTYPE);
            AlipayTradeWapPayRequest alipay_request=new AlipayTradeWapPayRequest();
            // 封装请求支付信息
            AlipayTradeWapPayModel model=new AlipayTradeWapPayModel();
            model.setOutTradeNo(productOrder.getOrderNumber());
            model.setSubject(subject);
            model.setTotalAmount(total_amount);
            model.setBody("");
            model.setTimeoutExpress("2m");
            model.setProductCode(product_code);
            alipay_request.setBizModel(model);
            // 设置异步通知地址
            alipay_request.setNotifyUrl(AlipayConfig.notify_url);
            // 设置同步地址
            alipay_request.setReturnUrl(AlipayConfig.return_url);
            // form表单生产
            String form = "";
            try {
                // 调用SDK生成表单
                form = client.pageExecute(alipay_request).getBody();
                response.setContentType("text/html;charset=" + AlipayConfig.CHARSET);
                response.getWriter().write(form);//直接将完整的表单html输出到页面
                response.getWriter().flush();
                response.getWriter().close();
            } catch (AlipayApiException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    private R dealOnlyPointsPay(JsProductOrder productOrder) {
            //单纯积分支付
            productOrder.setPayDatetime(LocalDateTime.now());
            productOrder.setPayDate(new Date());
            productOrder.setCashPayFee(productOrder.getTruePay());
            productOrder.setState(1);
            productOrderService.updateById(productOrder);
            //修改个人积分余额
            JsUsr usr = usrService.getById(productOrder.getUsrId());
            if(null!=usr){
                if(usr.getBalance().compareTo(productOrder.getUsePoints())==-1){
                    usr.setBalance(new BigDecimal("0"));
                }else {
                    usr.setBalance(usr.getBalance().subtract(productOrder.getUsePoints()));
                }
                usrService.updateById(usr);
            }
            List<JsProductOrderDetail> details = productOrderDetailService.list(new QueryWrapper<JsProductOrderDetail>().eq("order_id",productOrder.getId()));
            for(JsProductOrderDetail productOrderDetail:details){
                redisUtils.incr("jinshan:product:sale:"+productOrderDetail.getProductId());
            }
        return R.ok();
    }

    @ApiModelProperty("支付宝异步通知")
    @PostMapping("/notify")
    @UnAuthRequest
    public String notify(HttpServletRequest request) throws UnsupportedEncodingException, AlipayApiException {
        //获取支付宝POST过来反馈信息
        Console.log("支付宝通知");
        Map<String,String> params = new HashMap<String,String>();
        Map requestParams = request.getParameterMap();
        for (Iterator iter = requestParams.keySet().iterator(); iter.hasNext();) {
            String name = (String) iter.next();
            String[] values = (String[]) requestParams.get(name);
            String valueStr = "";
            for (int i = 0; i < values.length; i++) {
                valueStr = (i == values.length - 1) ? valueStr + values[i]
                        : valueStr + values[i] + ",";
            }
            //乱码解决，这段代码在出现乱码时使用。如果mysign和sign不相等也可以使用这段代码转化
            //valueStr = new String(valueStr.getBytes("ISO-8859-1"), "gbk");
            params.put(name, valueStr);
        }
        //商户订单号
        String out_trade_no = new String(request.getParameter("out_trade_no").getBytes("ISO-8859-1"),"UTF-8");
        //支付宝交易号
        String trade_no = new String(request.getParameter("trade_no").getBytes("ISO-8859-1"),"UTF-8");
        //交易状态
        String trade_status = new String(request.getParameter("trade_status").getBytes("ISO-8859-1"),"UTF-8");
        //获取支付宝的通知返回参数，可参考技术文档中页面跳转同步通知参数列表(以上仅供参考)//
        //计算得出通知验证结果
        //boolean AlipaySignature.rsaCheckV1(Map<String, String> params, String publicKey, String charset, String sign_type)
        boolean verify_result = AlipaySignature.rsaCheckV1(params, AlipayConfig.ALIPAY_PUBLIC_KEY, AlipayConfig.CHARSET, "RSA2");
        if(verify_result){
            //修改订单状态和积分余额，先判断支付的金额 和返回的余额相同
            Console.log("支付宝通知,订单Id是"+trade_no);
            //根据订单号去查询订单
            JsProductOrder order = productOrderService.getOne(new QueryWrapper<JsProductOrder>().eq("order_number",out_trade_no));
            if(null!=order){
                if(0==order.getState()){
                    order.setState(1);
                    order.setPayDatetime(LocalDateTime.now());
                    order.setPayDate(new Date());
                    order.setCashTradeNo(trade_no);
                    order.setCashPayFee(order.getTruePay());
                    productOrderService.updateById(order);
                    //修改个人积分余额
                    JsUsr usr = usrService.getById(order.getUsrId());
                    if(null!=usr){
                        if(usr.getBalance().compareTo(order.getUsePoints())==-1){
                            usr.setBalance(new BigDecimal("0"));
                        }else {
                            usr.setBalance(usr.getBalance().subtract(order.getUsePoints()));
                        }
                        usrService.updateById(usr);
                    }
                    List<JsProductOrderDetail> details = productOrderDetailService.list(new QueryWrapper<JsProductOrderDetail>().eq("order_id",order.getId()));
                    for(JsProductOrderDetail productOrderDetail:details){
                        redisUtils.incr("jinshan:product:sale:"+productOrderDetail.getProductId());
                    }
                }
            }
            return "success";
        }else {
            return "fail";
        }
    }

    @ApiModelProperty("列表")
    @RequestMapping("/list")
    public R list(@RequestParam Map<String, Object> params,HttpServletRequest request){
        Integer  userId = usrService.getUserIdByPhone(request.getAttribute("userId").toString());
        params.put("userId",userId);
        PageUtils page = productOrderService.queryPage(params);
        return R.ok().put("page", page);
    }



    @ApiModelProperty("详情")
    @RequestMapping("/info/{orderId}")
    public R info(@PathVariable("orderId") Integer orderId){
        JsProductOrder order = productOrderService.getById(orderId);
        List<Logistics.LastResultBean> resultBeans = new ArrayList<>();
        if(null!=order){
            List<JsProductOrderDetail>  list =  productOrderDetailService.list(new QueryWrapper<JsProductOrderDetail>()
                    .eq("order_id",order.getId()));
            order.setProductOrderDetails(list);
            //查询物流信息
            List<JsProductOrderExpress> wuliuList = productOrderExpressService.list(new QueryWrapper<JsProductOrderExpress>().eq("order_id",order.getId()));
            for(JsProductOrderExpress a:wuliuList){
                String json = a.getExpressInfo();
                if(StringUtils.isNotBlank(json)){
                    Logistics logistics = JSONUtil.toBean(json,Logistics.class);
                    Logistics.LastResultBean resultBean = new Logistics.LastResultBean();
                    resultBean.setData(logistics.getLastResult().getData());
                    resultBean.setNu(logistics.getLastResult().getNu());
                    resultBeans.add(resultBean);
                }
            }
            order.setWuliu(resultBeans);
        }
        return R.ok().put("data",order);
    }


    @ApiModelProperty("微信支付下单")
    @RequestMapping("/wx/pay")
    public R wxPay(@RequestBody Cart cart) throws WxPayException {
        if(null!=cart.getOrderId()){
            JsProductOrder productOrder = productOrderService.getById(cart.getOrderId());
            if(null!=productOrder){
                if(1==cart.getIsJsApi()){
                    JsUsr usr = usrService.getById(productOrder.getUsrId());
                    WxPayUnifiedOrderRequest request = new WxPayUnifiedOrderRequest();
                    request.setTradeType("JSAPI");
                    request.setBody("金山商城付款");
                    request.setOutTradeNo(productOrder.getOrderNumber());
                    request.setOpenid(usr.getOpenId());
                    JSONObject jsonObject = new JSONObject();
                    jsonObject.put("orderId",cart.getOrderId());
                    request.setTotalFee(BaseWxPayRequest.yuanToFen(productOrder.getTruePay().toString()));//元转成分
                    request.setAttach(jsonObject.toString());
                    request.setSpbillCreateIp("127.0.0.1");
                    request.setNotifyUrl("http://laijinshanplay.cn/js-shop/order/js-product-order/wx/pay/notify");
                    WxPayMpOrderResult wxPayMwebOrderResult= wxService.createOrder(request);
                    R r = new R();
                    r.put("data",wxPayMwebOrderResult);
                    return r;
                }else {
                    WxPayUnifiedOrderRequest request = new WxPayUnifiedOrderRequest();
                    request.setTradeType("MWEB");
                    request.setBody("金山商城付款");
                    request.setOutTradeNo(productOrder.getOrderNumber());
                    JSONObject jsonObject = new JSONObject();
                    jsonObject.put("orderId",cart.getOrderId());
                    request.setTotalFee(BaseWxPayRequest.yuanToFen(productOrder.getTruePay().toString()));//元转成分
                    request.setAttach(jsonObject.toString());
                    request.setSpbillCreateIp("127.0.0.1");
                    request.setNotifyUrl("http://laijinshanplay.cn/js-shop/order/js-product-order/wx/pay/notify");
                    WxPayMwebOrderResult wxPayMwebOrderResult= wxService.createOrder(request);
                    R r = new R();
                    r.put("data",wxPayMwebOrderResult);
                    return r;
                }
            }
        }
        return new R();
    }

    @ApiModelProperty("微信支付通知")
    @UnAuthRequest
    @RequestMapping("/wx/pay/notify")
    public String wxPayNotify(@RequestBody String xmlData)  {
        final WxPayOrderNotifyResult notifyResult;
        try {
            notifyResult = this.wxService.parseOrderNotifyResult(xmlData);
            String a = notifyResult.getAttach();//获取携带信息
            JSONObject jsonObject = JSONUtil.parseObj(a);
            String orderId = jsonObject.get("orderId").toString();//订单ID
            Console.log("微信通知,订单Id是"+orderId);
            //修改订单状态和下单人的余额
            JsProductOrder order = productOrderService.getOne(new QueryWrapper<JsProductOrder>().eq("id",orderId));
            dealAfterNofity(order);
            return WxPayNotifyResponse.success("成功");
        } catch (WxPayException e) {
            e.printStackTrace();
            return WxPayNotifyResponse.fail("系统错误");
        }
    }

    @ApiModelProperty("支付宝退款")
    @RequestMapping("/alipay/refund")
    public R aliRefund(HttpServletRequest request) throws AlipayApiException, UnsupportedEncodingException {
        //商户订单号和支付宝交易号不能同时为空。 trade_no、  out_trade_no如果同时存在优先取trade_no
        //商户订单号，和支付宝交易号二选一
        String out_trade_no = new String(request.getParameter("WIDout_trade_no").getBytes("ISO-8859-1"),"UTF-8");
        //支付宝交易号，和商户订单号二选一
        String trade_no = new String(request.getParameter("WIDtrade_no").getBytes("ISO-8859-1"),"UTF-8");
        //退款金额，不能大于订单总金额
        String refund_amount=new String(request.getParameter("WIDrefund_amount").getBytes("ISO-8859-1"),"UTF-8");
        //退款的原因说明
        String refund_reason=new String(request.getParameter("WIDrefund_reason").getBytes("ISO-8859-1"),"UTF-8");
        //标识一次退款请求，同一笔交易多次退款需要保证唯一，如需部分退款，则此参数必传。
        String  out_request_no=new String(request.getParameter("WIDout_request_no").getBytes("ISO-8859-1"),"UTF-8");
        /**********************/
        // SDK 公共请求类，包含公共请求参数，以及封装了签名与验签，开发者无需关注签名与验签
        AlipayClient client = new DefaultAlipayClient(AlipayConfig.URL, AlipayConfig.APPID, AlipayConfig.RSA_PRIVATE_KEY, AlipayConfig.FORMAT, AlipayConfig.CHARSET, AlipayConfig.ALIPAY_PUBLIC_KEY,AlipayConfig.SIGNTYPE);
        AlipayTradeRefundRequest alipay_request = new AlipayTradeRefundRequest();
        AlipayTradeRefundModel model=new AlipayTradeRefundModel();
        model.setOutTradeNo(out_trade_no);
        model.setTradeNo(trade_no);
        model.setRefundAmount(refund_amount);
        model.setRefundReason(refund_reason);
        model.setOutRequestNo(out_request_no);
        alipay_request.setBizModel(model);
        AlipayTradeRefundResponse alipay_response =client.execute(alipay_request);
        return new R().put("data",alipay_response);
    }

    @RequestMapping("/applyRefund")
    public R applyRefund(@RequestBody Cart cart){
        JsProductOrder productOrder = productOrderService.getById(cart.getOrderId());
        if(null!=productOrder){
            if(null!=productOrder.getExpressSignDatetime()){
                  if(!DateUtil.isIn(new Date(),productOrder.getExpressSignDatetime(),DateUtil.offsetDay(productOrder.getExpressSignDatetime(),7))){
                      return R.error("签收超过7天");
                  }
            }
        }
        productOrder.setId(cart.getOrderId());
        productOrder.setState(4);
        productOrderService.updateById(productOrder);
        return R.ok();
    }

    @ApiModelProperty("银联支付")
    @RequestMapping("/pay/yinlian")
    @UnAuthRequest
    public void payYinLian(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        //前台页面传过来的
        String orderId = req.getParameter("orderId");
        JsProductOrder order = productOrderService.getById(orderId);
        Map<String, String> requestData = new HashMap<String, String>();
       /***银联全渠道系统，产品参数，除了encoding自行选择外其他不需修改***/
        requestData.put("version", DemoBase.version);    //版本号，全渠道默认值
        requestData.put("encoding", DemoBase.encoding);  //字符集编码，可以使用UTF-8,GBK两种方式
        requestData.put("signMethod", SDKConfig.getConfig().getSignMethod()); //签名方法
        requestData.put("txnType", "01");                //交易类型 ，01：消费
        requestData.put("txnSubType", "01");              //交易子类型， 01：自助消费
        requestData.put("bizType", "000201");            //业务类型，B2C网关支付，手机wap支付
        requestData.put("channelType", "07");            //渠道类型，这个字段区分B2C网关支付和手机wap支付；07：PC,平板  08：手机
       /***商户接入参数***/
        requestData.put("merId", "113310054991097");                //商户号码，请改成自己申请的正式商户号或者open上注册得来的777测试商户号
        requestData.put("accessType", "0");              //接入类型，0：直连商户
        requestData.put("orderId",order.getOrderNumber());             //商户订单号，8-40位数字字母，不能含“-”或“_”，可以自行定制规则
        requestData.put("txnTime", DemoBase.getCurrentTime());        //订单发送时间，取系统时间，格式为YYYYMMDDhhmmss，必须取当前时间，否则会报txnTime无效
        requestData.put("currencyCode", "156");          //交易币种（境内商户一般是156 人民币）
        requestData.put("txnAmt", BaseWxPayRequest.yuanToFen(order.getTruePay().toString()).toString());                  //交易金额，单位分，不要带小数点
        //requestData.put("reqReserved", "透传字段");              //请求方保留域，如需使用请启用即可；透传字段（可以实现商户自定义参数的追踪）本交易的后台通知,对本交易的交易状态查询交易、对账文件中均会原样返回，商户可以按需上传，长度为1-1024个字节。出现&={}[]符号时可能导致查询接口应答报文解析失败，建议尽量只传字母数字并使用|分割，或者可以最外层做一次base64编码(base64编码之后出现的等号不会导致解析失败可以不用管)。
        //前台通知地址 （需设置为外网能访问 http https均可），支付成功后的页面 点击“返回商户”按钮的时候将异步通知报文post到该地址
        //如果想要实现过几秒中自动跳转回商户页面权限，需联系银联业务申请开通自动返回商户权限
        //异步通知参数详见open.unionpay.com帮助中心 下载  产品接口规范  网关支付产品接口规范 消费交易 商户通知
        requestData.put("frontUrl", DemoBase.frontUrl);
        //后台通知地址（需设置为【外网】能访问 http https均可），支付成功后银联会自动将异步通知报文post到商户上送的该地址，失败的交易银联不会发送后台通知
        //后台通知参数详见open.unionpay.com帮助中心 下载  产品接口规范  网关支付产品接口规范 消费交易 商户通知
        //注意:1.需设置为外网能访问，否则收不到通知    2.http https均可  3.收单后台通知后需要10秒内返回http200或302状态码
        //    4.如果银联通知服务器发送通知后10秒内未收到返回状态码或者应答码非http200，那么银联会间隔一段时间再次发送。总共发送5次，每次的间隔时间为0,1,2,4分钟。
        //    5.后台通知地址如果上送了带有？的参数，例如：http://abc/web?a=b&c=d 在后台通知处理程序验证签名之前需要编写逻辑将这些字段去掉再验签，否则将会验签失败
        requestData.put("backUrl", DemoBase.backUrl);
        // 订单超时时间。
        // 超过此时间后，除网银交易外，其他交易银联系统会拒绝受理，提示超时。 跳转银行网银交易如果超时后交易成功，会自动退款，大约5个工作日金额返还到持卡人账户。
        // 此时间建议取支付时的北京时间加15分钟。
        // 超过超时时间调查询接口应答origRespCode不是A6或者00的就可以判断为失败。
        requestData.put("payTimeout", new SimpleDateFormat("yyyyMMddHHmmss").format(System.currentTimeMillis() + 15 * 60 * 1000));
//////////////////////////////////////////////////
//       报文中特殊用法请查看 PCwap网关跳转支付特殊用法.txt
//////////////////////////////////////////////////
/**请求参数设置完毕，以下对请求参数进行签名并生成html表单，将表单写入浏览器跳转打开银联页面**/
        Map<String, String> submitFromData = AcpService.sign(requestData,DemoBase.encoding);  //报文中certId,signature的值是在signData方法中获取并自动赋值的，只要证书配置正确即可。
        String requestFrontUrl = SDKConfig.getConfig().getFrontRequestUrl();  //获取请求银联的前台地址：对应属性文件acp_sdk.properties文件中的acpsdk.frontTransUrl
        String html = AcpService.createAutoFormHtml(requestFrontUrl, submitFromData,DemoBase.encoding);   //生成自动跳转的Html表单
        LogUtil.writeLog("打印请求HTML，此为请求报文，为联调排查问题的依据："+html);
//将生成的html写到浏览器中完成自动跳转打开银联支付页面；这里调用signData之后，将html写到浏览器跳转到银联页面之前均不能对html中的表单项的名称和值进行修改，如果修改会导致验签不通过
        resp.getWriter().write(html);
    }

    @ApiModelProperty("银联异步通知")
    @RequestMapping("/yinlian/nofity")
    @UnAuthRequest
    public void  yinlianNofity(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        LogUtil.writeLog("BackRcvResponse接收后台通知开始");
        String encoding = req.getParameter(SDKConstants.param_encoding);
        // 获取银联通知服务器发送的后台通知参数
        Map<String, String> reqParam = FrontRcvResponse.getAllRequestParam(req);
        LogUtil.printRequestLog(reqParam);
        Map<String, String> valideData = null;
        if (null != reqParam && !reqParam.isEmpty()) {
            Iterator<Map.Entry<String, String>> it = reqParam.entrySet().iterator();
            valideData = new HashMap<String, String>(reqParam.size());
            while (it.hasNext()) {
                Map.Entry<String, String> e = it.next();
                String key = (String) e.getKey();
                String value = (String) e.getValue();
                value = new String(value.getBytes(encoding), encoding);
                valideData.put(key, value);
            }
        }
        //重要！验证签名前不要修改reqParam中的键值对的内容，否则会验签不过
        if (!AcpService.validate(valideData, encoding)) {
            LogUtil.writeLog("验证签名结果[失败].");
            //验签失败，需解决验签问题
        } else {
            LogUtil.writeLog("验证签名结果[成功].");
            //【注：为了安全验签成功才应该写商户的成功处理逻辑】交易成功，更新商户订单状态
            String orderId = valideData.get("orderId"); //获取后台通知的数据，其他字段也可用类似方式获取
            String queryId = valideData.get("queryId");
            Console.log("queryId是"+queryId);
            Console.log("orderId"+orderId);
            JsProductOrder order = productOrderService.getOne(new QueryWrapper<JsProductOrder>().eq("order_number", orderId));
            if(0==order.getState()){
                if(StringUtils.isNotBlank(queryId)){
                    order.setQueryId(queryId);
                }
                String respCode = valideData.get("respCode");
                Console.log(respCode);
                //判断respCode=00、A6后，对涉及资金类的交易，请再发起查询接口查询，确定交易成功后更新数据库。
                dealAfterNofity(order);
            }else {
                if (null != order) {
                    order.setState(5);
                    order.setRefundDatetime(new Date());
                    productOrderService.updateById(order);
                }
            }
            LogUtil.writeLog("BackRcvResponse接收后台通知结束");
            //返回给银联服务器http 200  状态码
            resp.getWriter().print("ok");
        }
    }


    @ApiModelProperty("银联退款异步通知")
    @RequestMapping("/yinlian/refund/nofity")
    @UnAuthRequest
    public void  refundNofity(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        LogUtil.writeLog("BackRcvResponse接收后台通知开始");
        String encoding = req.getParameter(SDKConstants.param_encoding);
        // 获取银联通知服务器发送的后台通知参数
        Map<String, String> reqParam = FrontRcvResponse.getAllRequestParam(req);
        LogUtil.printRequestLog(reqParam);
        Map<String, String> valideData = null;
        if (null != reqParam && !reqParam.isEmpty()) {
            Iterator<Map.Entry<String, String>> it = reqParam.entrySet().iterator();
            valideData = new HashMap<String, String>(reqParam.size());
            while (it.hasNext()) {
                Map.Entry<String, String> e = it.next();
                String key = (String) e.getKey();
                String value = (String) e.getValue();
                value = new String(value.getBytes(encoding), encoding);
                valideData.put(key, value);
            }
        }
        //重要！验证签名前不要修改reqParam中的键值对的内容，否则会验签不过
        if (!AcpService.validate(valideData, encoding)) {
            LogUtil.writeLog("验证签名结果[失败].");
            //验签失败，需解决验签问题
        } else {
            LogUtil.writeLog("验证签名结果[成功].");
            //【注：为了安全验签成功才应该写商户的成功处理逻辑】交易成功，更新商户订单状态
            String orderId = valideData.get("orderId"); //获取后台通知的数据，其他字段也可用类似方式获取
            String queryId = valideData.get("queryId");
            Console.log("queryId是"+queryId);
            Console.log("orderId"+orderId);
            JsProductOrder order = productOrderService.getOne(new QueryWrapper<JsProductOrder>().eq("order_number", orderId.substring(1)));
            if (null != order) {
                order.setState(5);
                order.setRefundDatetime(new Date());
                productOrderService.updateById(order);
            }
            LogUtil.writeLog("BackRcvResponse接收后台通知结束");
            //返回给银联服务器http 200  状态码
            resp.getWriter().print("ok");
        }
    }


    private void dealAfterNofity(JsProductOrder order) {
        if (null != order) {
            order.setState(1);
            order.setPayDatetime(LocalDateTime.now());
            order.setPayDate(new Date());
            order.setCashPayFee(order.getTruePay());
            productOrderService.updateById(order);
            //修改个人积分余额
            JsUsr usr = usrService.getById(order.getUsrId());
            if (null != usr) {
                if(usr.getBalance().compareTo(order.getUsePoints())==-1){
                    usr.setBalance(new BigDecimal("0"));
                }else {
                    usr.setBalance(usr.getBalance().subtract(order.getUsePoints()));
                }
                usrService.updateById(usr);
            }
        }
        List<JsProductOrderDetail> details = productOrderDetailService.list(new QueryWrapper<JsProductOrderDetail>().eq("order_id", order.getId()));
        for (JsProductOrderDetail productOrderDetail : details) {
            redisUtils.incr("jinshan:product:sale:" + productOrderDetail.getProductId());
        }
    }
}
